use crate::api::model::Check;
use crate::resp::HttpResponse;
use snafu::{ResultExt, ensure_whatever};

pub fn run_checks(resp: &HttpResponse, checks: &[Check]) -> crate::Result<()> {
    let mut parsed = None;

    for check in checks {
        match check {
            Check::Http2xx => {
                ensure_whatever!(
                    (200..300).contains(&resp.status),
                    "HTTP not success: status={} body_snippet={}",
                    resp.status,
                    snippet_utf8(resp.body.as_ref(), 300)
                );
            }
            Check::Code { field, success } => {
                let v = parse_once_json(resp, &mut parsed)?;
                let got = v.get(field).and_then(|x| x.as_i64());
                ensure_whatever!(
                    got == Some(*success),
                    "Business code mismatch: field={}, got={:?}, expect={} body_snippet={}",
                    field,
                    got,
                    success,
                    snippet_utf8(resp.body.as_ref(), 200)
                );
            }
            Check::Ok { field, success } => {
                let v = parse_once_json(resp, &mut parsed)?;
                let got = v.get(field).and_then(|x| x.as_bool());

                ensure_whatever!(
                    got == Some(*success),
                    "Business code mismatch: field={}, got={:?}, expect={},body_snippet={}",
                    field,
                    got,
                    success,
                    snippet_utf8(resp.body.as_ref(), 200)
                )
            }
        }
    }
    Ok(())
}

fn snippet_utf8(b: &[u8], max: usize) -> String {
    let s = String::from_utf8_lossy(b);
    let s = s.trim();
    if s.len() <= max {
        s.to_owned()
    } else {
        format!("{}…", &s[..max])
    }
}

fn parse_once_json<'a>(
    resp: &HttpResponse,
    cache: &'a mut Option<serde_json::Value>,
) -> crate::Result<&'a serde_json::Value> {
    if cache.is_none() {
        *cache = Some(resp.json().whatever_context("Could not parse json")?);
    }
    Ok(cache.as_ref().unwrap())
}
